package com.googlecode.horncomb.web.spring.mvc;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;

import com.googlecode.horncomb.util.lang.GenericsUtil;

/**
 * 扩展Sping MVC的SimpleFormController
 * @author Daniel
 */
public class BaseSimpleFormController extends SimpleFormController {
	protected final static String PREFIX_REDIRECTVIEW = "redirect";
	protected final static String VIEW_TYPE_TOKEN = ":";

	protected String urlEncoding = "UTF-8"; //$NON-NLS-1$
	/**
	 * 日期格式
	 */
	protected String dateFormate = "yyyy-MM-dd";

	public BaseSimpleFormController() {
		super();
		// 新表单（get）时候也进行构造
		this.setBindOnNewForm(true);
	}

	@Override
	protected void initApplicationContext() {
		super.initApplicationContext();
		// modify command to "form"
		if (DEFAULT_COMMAND_NAME.equals(this.getCommandName())
				|| !StringUtils.hasText(this.getCommandName())) {
			this.setCommandName("form");
		}

		if (this.getCommandClass() == null) {
			Class<?> type = GenericsUtil.getGenericClass(this.getClass());
			if (type != null) {
				this.setCommandClass(type);
			}
		}

		if (this.getCommandClass() == null) {
			throw new IllegalStateException(
					"Cannot create command without commandClass being set - "
							+ "either set commandClass or (in a form controller) override formBackingObject");
		}
	}

	/**
	 * 初始化绑定日期、数值编辑器
	 * @see org.springframework.web.servlet.mvc.BaseCommandController#initBinder(javax.servlet.http.HttpServletRequest,
	 *      org.springframework.web.bind.ServletRequestDataBinder)
	 */
	@Override
	protected void initBinder(HttpServletRequest request,
			ServletRequestDataBinder binder) throws Exception {
		// 允许为空的绑定
		// bind DateEditor
		SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormate);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(
				dateFormat, true));
		// bind IntegerEditor
		binder.registerCustomEditor(Integer.class, new CustomNumberEditor(
				Integer.class, true));
		// bind LongEditor
		binder.registerCustomEditor(Long.class, new CustomNumberEditor(
				Long.class, true));
		// bind DoubleEditor
		binder.registerCustomEditor(Double.class, new CustomNumberEditor(
				Double.class, true));
		// bind ByteArrayMultipartFileEditor for file upload
		binder.registerCustomEditor(byte[].class,
				new ByteArrayMultipartFileEditor());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected ModelAndView onSubmit(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {
		ModelAndView mv = onSubmit(command);
		if (mv != null) {
			// simplest onSubmit variant implemented in custom subclass
			return mv;
		} else {
			return this.getSuccessModeAndView(request, command, errors, null);
		}
	}

	@SuppressWarnings("unchecked")
	protected ModelAndView getSuccessModeAndView(HttpServletRequest request,
			Object command, BindException errors, Map controlModel)
			throws Exception {
		if (getSuccessView() != null && getSuccessView().equals(getFormView())) {
			setSuccessView(null);
		}
		// 原则上优先服从successView，然后是formView
		if (getSuccessView() != null) {
			ModelAndView mav = resolveView(getSuccessView(), controlModel);
			Map<String, Object> ref = this.getSuccessViewReference(request,
					command, errors);
			if (ref != null) {
				mav.addAllObjects(ref);
			}
			return mav;
		} else if (getFormView() != null) {
			return showForm(request, errors, getFormView(), controlModel);
		} else {
			throw new ServletException("formView, successView aren't set");
		}
	}

	/**
	 * resolve view
	 * @param viewName
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected static ModelAndView resolveView(String viewName, Map controlModel) {
		Assert.hasText(viewName);
		viewName = StringUtils.trimWhitespace(viewName);
		if (StringUtils.startsWithIgnoreCase(viewName, PREFIX_REDIRECTVIEW)) {
			String subvn = StringUtils.trimWhitespace(viewName
					.substring(PREFIX_REDIRECTVIEW.length()));
			if (StringUtils.startsWithIgnoreCase(subvn, VIEW_TYPE_TOKEN)) {
				return new ModelAndView(new RedirectView(StringUtils
						.trimWhitespace(subvn.substring(VIEW_TYPE_TOKEN
								.length())), true));
			}
		}
		return new ModelAndView(viewName, controlModel);
	}

	protected Map<String, Object> getSuccessViewReference(
			HttpServletRequest request, Object command, Errors error)
			throws Exception {
		return new HashMap<String, Object>();
	}

	public void setDateFormate(String dateFormate) {
		this.dateFormate = dateFormate;
	}

	/**
	 * @param urlEncoding
	 *            the urlEncoding to set
	 */
	public void setUrlEncoding(String urlEncoding) {
		this.urlEncoding = urlEncoding;
	}
}
